#pragma once

#include <iostream>
#include <string>
#include <thread>
#include <functional>
#include <unordered_map>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include "Log.hpp"
#include "UnCopy.hpp"
#include "commen.hpp"
#include "InetAddress.hpp"
#include "ThreadPool.hpp"

const int default_backlog = 5;

using task_t = std::function<void()>;

class TcpServer : public UnCopy
{
public:
    TcpServer(uint16_t port) : _port(port), _isRunning(false)
    {
        init();
    }

    void init()
    {
        // 创建socket
        _listen_fd = socket(AF_INET, SOCK_STREAM, 0); //SOCK_STREAM 流式套接
        if(_listen_fd < 0)
        {
            log.message(FATAL, "socket error, %d: %s", errno, strerror(errno));
            exit(SOCKET_ERR);
        }
        log.message(INFO, "socket success, socket fd: %d", _listen_fd);

        // 若服务器主动断开连接，与客户端四次挥手，进入TIME_WAIT，此时连接尚未彻底释放，端口仍在使用中，服务无法立即重启
        // [FATAL][2024/8/9 22:43.35][8061] bind error, 98: Address already in use
        // 设置套接字地址复用，断开连接进TIME_WAIT，但是不等，可以立即重启
        int opt = 1; //选项
        setsockopt(_listen_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt)); //地址复用

        // 填写本端网络信息
        struct sockaddr_in local;
        bzero(&local, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(_port);
        local.sin_addr.s_addr = INADDR_ANY;

        // 绑定 网络信息sockaddr 与 文件信息socket
        if(bind(_listen_fd, (struct sockaddr*)&local, sizeof(local)) != 0)
        {
            log.message(FATAL, "bind error, %d: %s\n", errno, strerror(errno));
            exit(BIND_ERR);
        }
        log.message(INFO, "bind success...");

        // 设置socket为监听状态
        
        // listen获取到连接，进行三次握手后，管理进内核全连接队列，等待上层accept（饭馆等号）
        // 队列节点数上限为backlog+1，达到上限后，拒绝到来的连接

        // 半连接：listen成功，未三次握手，状态为SYN_RECV或SYN_SENT
        // 全连接：三次握手成功，状态为ESTABLISHED
        if(listen(_listen_fd, default_backlog) != 0)
        {
            log.message(FATAL, "listen error, %d: %s\n", errno, strerror(errno));
            exit(LISTEN_ERR);
        }
        log.message(INFO, "listen success...");

        ThreadPool<task_t>::getInstance()->start();
    }

    void start()
    {
        _isRunning = true;
        while(true)
        {
            // 获取连接
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            int communi_fd = accept(_listen_fd, (struct sockaddr*)&peer, &len); //该socket用于通信
            if(communi_fd < 0)
            {
                log.message(WARN, "accept socket error, %d: %s, accept continue...", errno, strerror(errno));
                continue;
            }
            log.message(INFO, "accept success...");

            // 多线程
            // 主线程可能先于新线程执行，导致communi_fd被更新，需提前保存一份communi_fd
            int communi_fd_tmp = communi_fd;
            task_t task = std::bind([this](int fd, InetAddress client_addr) {
                doProcess(fd, client_addr);
                close(fd);
            }, communi_fd_tmp, InetAddress(peer));
            ThreadPool<task_t>::getInstance()->submit(task);
        }
    }

    void doProcess(int communi_fd, InetAddress client_addr)
    {
        char buffer[1024];
        while(true)
        {
            ssize_t n = read(communi_fd, buffer, sizeof(buffer)-1);
            if(n > 0)
            {
                buffer[n] = '\0';
                std::cout << client_addr.getAddressInfo() << " resp=" << buffer << std::endl;
                std::string response = buffer;
                write(communi_fd, response.c_str(), response.size()); //IO类函数，内部会做网络序列转换
            }
            else if(n == 0) //返回0，意味着对端关闭了连接（类比管道：写端关闭，读端read直接返回0）
            {
                log.message(INFO, "socket read to the end, client is quit...");
                break;
            }
            else
            {
                log.message(ERROR, "socket read error, %d: %s", errno, strerror(errno));
            }
        }
    }

    ~TcpServer()
    {
        close(_listen_fd);
    }
private:
    bool _isRunning;
    uint16_t _port;
    int _listen_fd; //该socket用于连接监听
};







// 单进程
// void start()
// {
//     _isRunning = true;
//     while(true)
//     {
//         // 获取连接
//         struct sockaddr_in peer;
//         socklen_t len = sizeof(peer);
//         int communi_fd = accept(_listen_fd, (struct sockaddr*)&peer, &len); //该socket用于通信
//         if(communi_fd < 0)
//         {
//             log.message(WARN, "accept socket error, %d: %s, accept continue...", errno, strerror(errno));
//             continue;
//         }
//         log.message(INFO, "accept success...");
//
//         doProcess(communi_fd);
//         close(communi_fd);
//     }
// }

// 多进程 父子并行方案1：孙进程
// void start()
// {
//     _isRunning = true;
//     while(true)
//     {
//         // 获取连接
//         struct sockaddr_in peer;
//         socklen_t len = sizeof(peer);
//         int communi_fd = accept(_listen_fd, (struct sockaddr*)&peer, &len); //该socket用于通信
//         if(communi_fd < 0)
//         {
//             log.message(WARN, "accept socket error, %d: %s, accept continue...", errno, strerror(errno));
//             continue;
//         }
//         log.message(INFO, "accept success...");
//
//         pid_t fid = fork();
//         if(fid < 0) //error
//         {
//             close(communi_fd);
//         }
//         else if(fid == 0) //child，做读写通信
//         {
//             close(_listen_fd);
//             if(fork() > 0) exit(0); //创建孙进程并退出，父进程直接回收，父孙之间形成完全并行，孙进程变孤儿，由OS回收
//             doProcess(communi_fd);
//             close(communi_fd);
//             exit(0);
//         }
//         else //father，做连接监听
//         {
//             close(communi_fd);
//             pid_t wid = waitpid(fid, nullptr, 0); //等待子进程，会使父子形成串行
//             if(wid == id)
//             {}
//         }
//     }
// }

// 多进程 父子并行方案2：忽略回收信号
// void start()
// {
//     _isRunning = true;
//     while(true)
//     {
//         // 获取连接
//         struct sockaddr_in peer;
//         socklen_t len = sizeof(peer);
//         int communi_fd = accept(_listen_fd, (struct sockaddr*)&peer, &len); //该socket用于通信
//         if(communi_fd < 0)
//         {
//             log.message(WARN, "accept socket error, %d: %s, accept continue...", errno, strerror(errno));
//             continue;
//         }
//         log.message(INFO, "accept success...");
//         
//         signal(SIGCHLD, SIG_IGN); //忽略子进程回收信号，子进程不僵尸
//         pid_t fid = fork();
//         if(fid < 0) //error
//         {
//             close(communi_fd);
//         }
//         else if(fid == 0) //child，做读写通信
//         {
//             close(_listen_fd);
//             doProcess(communi_fd);
//             close(communi_fd);
//             exit(0);
//         }
//         else //father，做连接监听
//         {
//             close(communi_fd);
//         }
//     }
// }

// 多线程
// void start()
// {
//     _isRunning = true;
//     while(true)
//     {
//         // 获取连接
//         struct sockaddr_in peer;
//         socklen_t len = sizeof(peer);
//         int communi_fd = accept(_listen_fd, (struct sockaddr*)&peer, &len); //该socket用于通信
//         if(communi_fd < 0)
//         {
//             log.message(WARN, "accept socket error, %d: %s, accept continue...", errno, strerror(errno));
//             continue;
//         }
//         log.message(INFO, "accept success...");

//         // 主线程可能先于新线程执行，导致communi_fd被更新，需提前保存一份communi_fd
//         int communi_fd_tmp = communi_fd;
//         std::thread t([this](int fd, InetAddress client_addr) {
//             doProcess(fd, client_addr);
//             close(fd);
//         }, communi_fd_tmp, InetAddress(peer));
//         t.detach();
//     }
// }